package instrumentation;


import java.util.*;


import util.DebugConfig;
import util.GAConfig;
import util.Log;
import ga.Individual;
import operations.Operation;
import instrumentation.CoverageBranchTarget;

public class TrackerCoverage {

	private static TrackerCoverage tracker;
	// Branch Coverage
	public int[] branches= new int[2000];
	public  Map<Integer,Individual> branchCoveredByIndividual = new HashMap<Integer, Individual>() ;
	public  Map<Integer,Individual> branchNotCoveredByIndividual = new HashMap<Integer, Individual>() ;
	public  Map<Integer,Float> individualBestAcievedDistance = new HashMap<Integer, Float>() ;

	private double distance ;
	private  CoverageBranchTarget condTarget ;
	private  Map<Integer,CoverageBranchTarget> trackers = new LinkedHashMap<Integer,CoverageBranchTarget>();

	
	public Map<Integer,CoverageBranchTarget> getTrackers(){
		return tracker.trackers;
	}

	public void setTrackerToNull(){
		tracker=null;
	}
	
    public static TrackerCoverage getTracker() {
		if(tracker == null){
			tracker = new TrackerCoverage();
			tracker.trackers = new LinkedHashMap<Integer,CoverageBranchTarget>();

			}
		return tracker;
	}

	public  TrackerCoverage() {
		reset();
	}

	public void reset() {
		branches= new int[2000];
		condTarget=null;
		individualBestAcievedDistance.clear();
	}
	public  void bestDistanceReachedBranch(Individual temp){//List<Operation> initialConstructors,List<Operation> ops){
		
		for( Integer br : branchNotCoveredByIndividual.keySet()){
			if(branchNotCoveredByIndividual.get(br)==null){
				Individual bestReachedBr=temp.clone();//new Individual(initialConstructors,ops);
				bestReachedBr=bestReachedBr.clone();
				condTarget = tracker.trackers.get(br);
				bestReachedBr.setBranchDistance(condTarget.getQuality());
				branchNotCoveredByIndividual.put(br, bestReachedBr);	
			}
			else{
				Individual bestReachedIndividual=branchNotCoveredByIndividual.get(br);
				condTarget = tracker.trackers.get(br);
				if(bestReachedIndividual.getBranchDistance()<condTarget.getQuality()){
					bestReachedIndividual=temp.clone();//new Individual(initialConstructors,ops);
					bestReachedIndividual.setBranchDistance(condTarget.getQuality());
					branchNotCoveredByIndividual.put(br, bestReachedIndividual);		
				}
					
			}
		}
		
	}
	public  void branchHit(int branchId) {
		if(!tracker.trackers.containsKey(branchId)){
			condTarget = new CoverageBranchTarget(branchId);
			tracker.trackers.put(branchId,condTarget);
			branchNotCoveredByIndividual.put(branchId, null);
		}
		individualBestAcievedDistance.put(branchId, condTarget.getQuality());


	}
	public void branchCovered(int branchId) {
//		if(branchId==5)
//			System.err.println("Branch Covered "+ branchId);
		if(trackers.containsKey(branchId)){
			branches[branchId]++;
			CoverageBranchTarget tempCondTarget=(CoverageBranchTarget)tracker.trackers.get(branchId);
			if(tempCondTarget.getBranchId()==branchId)
				tempCondTarget.setDistance(-1);
			branchNotCoveredByIndividual.remove(branchId);
		}
	}
	public  int getConditionTargetId(int branchId) {
		if(trackers.containsKey(branchId)){
			 condTarget=(CoverageBranchTarget)tracker.trackers.get(branchId);
			if(condTarget.getBranchId()==branchId)
				return branchId;
		}
		return -1;
	}
	
	public void setConditionTargetDistance(double distance) {
			condTarget.setDistance(distance);
	}

	//null==null=> 1(v1=0; v2=0)+1=>1; quality=0.5 Reference" Arucuri Thesis Page: 26
	public void calculateConditionTargetDistance(double v1, double v2) {
		distance=(Math.abs(v1 - v2))+1;
		if(condTarget.getQuality()!=0)
			condTarget.setDistance(distance);

	}

	public float getTotalCoveredBranch(List<Integer> branchs) {
		float CoveredBR=0;
		List<Integer> keys = new ArrayList<Integer>( tracker.trackers.keySet());   
		for( int i=0; i<keys.size();i++){			
			CoverageBranchTarget condTarget=(CoverageBranchTarget)tracker.trackers.get(keys.get(i));
			if(condTarget.getQuality()==0 && branchs.contains(keys.get(i)))
				CoveredBR++;
//			else
//				if(condTarget.getQuality()!=1)
//					CoveredBR+=condTarget.getQuality();
		}
		
		return CoveredBR;	
	}

	public float [] calculateFitness(Individual test,Set<Integer> branchToBeCovered) {
		float [] fitness= new float[2]; 
		float totalCoverageBranch=0.f;
		float branchDistance=0;
		List<Integer> debug=new LinkedList<Integer>();
		for (int branchId = 0; branchId < branches.length; branchId++) {
			if(!branchToBeCovered.contains(branchId))continue;
			if (branches[branchId] > 0) {
				debug.add(branchId);

				totalCoverageBranch++;		
				if(!branchCoveredByIndividual.containsKey(branchId)){
					branchCoveredByIndividual.put(branchId, test.clone());//)[branchId]=test.clone();
					branchNotCoveredByIndividual.remove(branchId);
				}
				
			}
			else{
				condTarget=(CoverageBranchTarget)tracker.trackers.get(branchId);
				if(condTarget!=null)
				if(condTarget.getQuality()!=1 )
					branchDistance+=condTarget.getQuality();
			}

		}

		 fitness[0]=totalCoverageBranch+(0.5f * (1 / (1.0f + test.getSize())));
		 fitness[1]=branchDistance;

		 //debug
			if(DebugConfig.executerClass){
				Log.log(debug.toString());
				Log.log(Arrays.toString(fitness));
				Log.log("\n");		
			}
		return fitness;
		
	}	
	public float [] calculateHCFitness(Individual test,int BR) {
		float [] fitness= new float[2]; 
		float totalCoverageBranch=0.f;
		float branchDistance=1;

		List<Integer> debug=new LinkedList<Integer>();
		for (int branchId = 0; branchId < branches.length; branchId++) {
			if (branches[branchId] > 0) {
				debug.add(branchId);
				totalCoverageBranch++;		
				if(!branchCoveredByIndividual.containsKey(branchId)){
					branchCoveredByIndividual.put(branchId, test.clone());//)[branchId]=test.clone();
					branchNotCoveredByIndividual.remove(branchId);
				}
				
			}
			else{
				if(BR!=branchId) continue;
				condTarget=(CoverageBranchTarget)tracker.trackers.get(BR);
				if(condTarget!=null)
					branchDistance=condTarget.getQuality();
			}

		}

		 fitness[0]=totalCoverageBranch;
		 fitness[1]=branchDistance;

		 //debug
			if(DebugConfig.executerClass){
				Log.log(debug.toString());
				Log.log(Arrays.toString(fitness));
				Log.log("\n");		
			}
		return fitness;
		
	}	

	public String displayCondition(){
		List<Integer> keys = new ArrayList<Integer>( tracker.trackers.keySet());  
		StringBuilder branchs =new StringBuilder();
		for( int i=0; i<keys.size();i++){		
			String predicate;
			CoverageBranchTarget condTarget=(CoverageBranchTarget)tracker.trackers.get(keys.get(i));
			predicate =  condTarget.toString() + "\n";
			branchs.append(predicate.toString());
		}
		return branchs.toString();
	}
	
	public  void summarizeCoverage(List<Integer> branchs) {
		HashSet<String> suiteBR= new HashSet<String>();
				for (Integer i : tracker.branchCoveredByIndividual.keySet()) {
					if(! branchs.contains(i))continue;
					Individual test=	tracker.branchCoveredByIndividual.get(i);
					suiteBR.add(test.toString() + "\n\t size: "+ test.getSize());
				}
				System.out.println ("SUMMARY: " + suiteBR.size() +
			    " tests needed for total branch coverage:");
			    if(GAConfig.SaveData)
			    	Log.log("SUMMARY: " + suiteBR.size() +" tests needed for total branch coverage:");
			    
				for (Iterator<String> i = suiteBR.iterator(); i.hasNext();) {
					String test=i.next();
//				    System.out.println("BR Test:" + test);
//				    if(GAConfig.SaveData)
//				    	Log.log("Test:\n" + test.toString());
				}

	}

	  public static final String lineSep = System.getProperty("line.separator");
		public String displayCoveredBranchs(List<Integer> branchs) {
			int CoveredBR=0;
			int unCoveredBR=0,idx=0;

			StringBuilder coverbranchs =new StringBuilder();
			 int [] BRCovered = new int[tracker.trackers.size()];
			 int [] BRUnCovered = new int[tracker.trackers.size()];
			List<Integer> keys = new ArrayList<Integer>( tracker.trackers.keySet());   
			for( int i=0; i<keys.size();i++){			
				if(! branchs.contains(keys.get(i)))continue;
				CoverageBranchTarget condTarget=(CoverageBranchTarget)tracker.trackers.get(keys.get(i));
				if(condTarget.getQuality()==0){
					CoveredBR++;
					BRCovered[idx++]=keys.get(i);
				}
				else{
					BRUnCovered[unCoveredBR]=keys.get(i);
					unCoveredBR++;
				}

			}
	//		coverbranchs.append("Total Branches  ( " + keys.size()  +" ):");
	//		coverbranchs.append(lineSep);
			coverbranchs.append("BR Covered SUMMARY ( " + CoveredBR  +" ):");
			coverbranchs.append(lineSep);
			 if (!GAConfig.Silent) {
				Arrays.sort(BRCovered);
				for (int i = 0; i < BRCovered.length; i++)
					if (BRCovered[i] != 0)
						coverbranchs.append("B" + BRCovered[i] + " ");
				coverbranchs.append(lineSep);
			}
			 coverbranchs.append("BR unCovered SUMMARY ( " + unCoveredBR  +" ): ");
			 coverbranchs.append(lineSep);
				Arrays.sort(BRUnCovered);
				for (int i = 0; i < BRUnCovered.length; i++)
					if (BRUnCovered[i] != 0){
						coverbranchs.append("B" + BRUnCovered[i] + " ");
					}
				coverbranchs.append(lineSep);
			return coverbranchs.toString(); 
		}

	@Override
	public String toString() {
		int CoveredBR=0;
		int unCoveredBR=0,idx=0;

		StringBuilder branchs =new StringBuilder();
		 int [] BRCovered = new int[tracker.trackers.size()];
		 int [] BRUnCovered = new int[tracker.trackers.size()];
		List<Integer> keys = new ArrayList<Integer>( tracker.trackers.keySet());   
		for( int i=0; i<keys.size();i++){			
			CoverageBranchTarget condTarget=(CoverageBranchTarget)tracker.trackers.get(keys.get(i));
			if(condTarget.getQuality()==0){
				CoveredBR++;
				BRCovered[idx++]=keys.get(i);
			}
			else{
				BRUnCovered[unCoveredBR]=keys.get(i);
				unCoveredBR++;
			}

		}
		 branchs.append("Total Branches  ( " + keys.size()  +" ):");
		 branchs.append(lineSep);
		 branchs.append("BR Covered SUMMARY ( " + CoveredBR  +" ):");
		 branchs.append(lineSep);
		 if (!GAConfig.Silent) {
			Arrays.sort(BRCovered);
			for (int i = 0; i < BRCovered.length; i++)
				if (BRCovered[i] != 0)
					branchs.append("B" + BRCovered[i] + " ");
			branchs.append(lineSep);
		}
		branchs.append("BR unCovered SUMMARY ( " + unCoveredBR  +" ): ");
		branchs.append(lineSep);
			Arrays.sort(BRUnCovered);
			for (int i = 0; i < BRUnCovered.length; i++)
				if (BRUnCovered[i] != 0){
					branchs.append("B" + BRUnCovered[i] + " ");
				}
			branchs.append(lineSep);
		return branchs.toString(); 
	}
	


	
}